﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Mindfor.Web.Data;
using Mindfor.Web.Routing;
using NHibernate;
using NHibernate.Linq;
using Mindfor.Web.Models;

namespace Mindfor.Web.Mvc.Controllers
{
	/// <summary>
	/// Contains actions for administrating users.
	/// </summary>
	[CmsAuthorize(CmsRoles.UsersManager)]
	public class UsersController : CmsController
	{
		/// <summary>
		/// Returns page result which renders List view.
		/// Sets model with users list;
		/// </summary>
		[UrlRoute(1, ""), UrlRouteDefault(1, "page", 1)]
		[UrlRoute(2, "Page{page}"), UrlRouteConstraint(2, "page", Constraints.IsInt)]
		[AdminLink("Пользователи", "Cистемные")]
		public virtual ActionResult Index(int page, string loginFilter)
		{
			int itemsPerPage = 20;
			int pageIndex = page - 1;
			if (pageIndex < 0)
				return HttpNotFound();

			int itemsCount = Data.Query<User>().Count();
			IQueryable<User> query = Data.Query<User>();
			if (!String.IsNullOrEmpty(loginFilter))
			{
				query = query.Where(r => r.Login.Contains(loginFilter));
				ViewData["LoginFilter"] = loginFilter;
			}

			ViewData["IsAdmin"] = User.IsInRole(CmsRoles.Administrator);
			ViewData["Pager"] = new PagerModel(itemsCount, itemsPerPage);
			ViewData.Model = query
				.OrderBy(u => u.Login)
				.Skip(pageIndex * itemsPerPage)
				.Take(itemsPerPage)
				.ToList();
			return View();
		}

		/// <summary>
		/// Calls edit with userId = null.
		/// </summary>
		[HttpGet]
		[LoadModelState]
		public virtual ActionResult Add()
		{
			return Edit(null);
		}

		/// <summary>
		/// Adds new user.
		/// </summary>
		[HttpPost]
		public virtual ActionResult Add(string login, string pwd, string confirmPwd, string email,
			string regDate, bool isApproved, bool isBanned, FormCollection form)
		{
			return Edit(null, login, pwd, confirmPwd, email, regDate, isApproved, isBanned, form);
		}

		/// <summary>
		/// Returns page result which renders Edit view.
		/// Sets model with IUser entity;
		/// </summary>
		[UrlRoute("{id}/Edit")]
		[UrlRouteConstraint("id", Constraints.IsInt)]
		[UrlSubAction("Index")]
		[LoadModelState]
		[HttpGet]
		public virtual ActionResult Edit(int? id)
		{
			User user = null;
			if (id.HasValue)
			{
				user = Data.Get<User>(id.Value);
				if (user == null)
					return HttpNotFound();
				if (user.IsInRole(CmsRoles.Administrator) && !User.IsInRole(CmsRoles.Administrator))
					return HttpAccessDenied();

				ViewData.Model = user;
			}
			else
			{
				ModelState.SetDefaultModelValue("IsApproved", true);
				ModelState.SetDefaultModelValue("RegDate", DateTime.UtcNow);
			}

			// Non-Administrator cannot set administrator's role
			IQueryable<Role> rolesQuery = Data.Query<Role>();
			if (!User.IsInRole(CmsRoles.Administrator))
				rolesQuery = rolesQuery.Where(r => r.Name.ToLower() != CmsRoles.Administrator.ToString().ToLower());

			ViewData["Roles"] = rolesQuery
				.OrderBy(r => r.FriendlyName)
				.ToList();
			ViewData["ReturnUrl"] = Url.Action("Index");

			// load more data
			return View("Edit");
		}

		/// <summary>
		/// Edits or creats user.
		/// Redirects to List view.
		/// </summary>
		[HttpPost]
		public virtual ActionResult Edit(int? id, FormCollection form)
		{
			User u;
			if (id != null)
			{
				u = Data.Get<User>(id.Value);
				if (u == null)
					return HttpNotFound();
				if (u.IsInRole(CmsRoles.Administrator) && !User.IsInRole(CmsRoles.Administrator))
					return HttpAccessDenied();
			}
			else
				u = new User();

			// check
			if (String.IsNullOrEmpty(login))
				ModelState.AddModelError("Login", "Введите логин");
			else
			{
				IUser u = Data.Users.GetUser(login, null);
				if (u != null && u.Id != id)
					ModelState.AddModelError("Login", "Логин уже занят. Введите другой");
			}

			if (!id.HasValue && String.IsNullOrEmpty(pwd))
				ModelState.AddModelError("Pwd", "Введите пароль");
			else if (pwd != confirmPwd)
				ModelState.AddModelError("", "Пароль и повтор пароля не совпадают");

			if (String.IsNullOrEmpty(email))
				ModelState.AddModelError("Email", "Введите e-mail");
			else
			{
				IUser u = Data.Users.GetUserByEmail(email);
				if (u != null && u.Id != id)
					ModelState.AddModelError("Email", "E-mail уже занят. Введите другой");
			}

			DateTime reg = DateTime.UtcNow;
			if (String.IsNullOrEmpty(regDate))
				ModelState.AddModelError("regDate", "Введите дату регистрации");
			else if (!DateTime.TryParse(regDate, out reg))
				ModelState.AddModelError("regDate", "Неверная дата регистрации");

			// check is ok
			if (ModelState.IsValid)
			{
				// get or create user
				IUser user;
				if (id.HasValue)
				{
					user = Data.Users.GetUser(id.Value);
					if (user == null)
						return HttpNotFound();
				}
				else
				{
					user = Data.Users.CreateUser();
					Data.Users.AddUser(user);
				}

				// update values
				user.Login = login;
				if (!String.IsNullOrEmpty(pwd))
					user.ChangePassword(pwd);
				user.Email = email;
				user.IsApproved = isApproved;
				user.IsBanned = isBanned;
				user.RegDate = reg;
				user.LastActivityDate = DateTime.UtcNow;
				Data.SubmitChanges();

				// edit roles
				foreach (Role role in Data.Users.GetRoles())
				{
					if (role.Name.ToLower() == CmsRoles.Administrator.ToString().ToLower() 
							&& !User.IsInRole(CmsRoles.Administrator))
						continue;

					string roleId = "Role" + role.Id;
					bool addRole = form.EvalBool(roleId) ?? false;
					bool hasRole = user.Roles.Any(r => r.Id == role.Id);
					if (hasRole && !addRole)
						Data.Users.RemoveRoleFromUser(user, role);
					else if (!hasRole && addRole)
						Data.Users.AddRoleToUser(user, role);
				}
				Data.SubmitChanges();

				// call methond for extension
				EditSuccessful(user);
				
				// redirect
				int page = Session.EvalInt("User.Page") ?? 1;
				return RedirectToAction("Index", new { page });
			}
			else
			{
				// save & redirect
				this.SaveFormToTempData();
				this.SaveModelState();
				return RedirectToAction(id.HasValue ? "Edit" : "Add");
			}
		}

		/// <summary>
		/// Deletes user and redirect to List action.
		/// </summary>
		[UrlRoute("{id}/Delete", false)]
		[UrlRouteConstraint("id", Constraints.IsInt)]
		[LoadFormAndModelState]
		public virtual ActionResult Delete(int id, string returnUrl)
		{
			IUser u = Data.Users.GetUser(id);
			if (u == null)
				return HttpNotFound();

			if (u.IsInRole(CmsRoles.Administrator) && !User.IsInRole(CmsRoles.Administrator))
				return AccessDenied();

			Data.Users.DeleteUser(u);
			Data.SubmitChanges();

			int page = Session.EvalInt("User.Page") ?? 1;
			return string.IsNullOrEmpty(returnUrl) ? (ActionResult)RedirectToAction("Index", new { page }) : (ActionResult)Redirect(returnUrl);
		}
	}
}
